#include "Main.h"

VOID unit_finalize(JSContext *cx, JSObject *obj)
{
	myUnit* lpUnit = (myUnit*)JS_GetPrivate(cx, obj);

	if(lpUnit)
		delete lpUnit;
}

INT unit_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
	myUnit* lpUnit = (myUnit*)JS_GetPrivate(cx, obj);

	if(!lpUnit || IsBadReadPtr(lpUnit, sizeof(myUnit)) || lpUnit->_dwPrivateType != PRIVATE_UNIT)
		return JS_TRUE;

	Unit* pUnit = FindUnit(lpUnit->dwId1, lpUnit->dwId2);

	if(!pUnit)
		return JS_TRUE;

	UnitCoords Coords;

	switch(JSVAL_TO_INT(id))
	{
		case UNIT_NAME:
			*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, GAME_GetUnitName(pUnit->dwClassId)));
		break;
		case UNIT_HP:
			*vp = INT_TO_JSVAL(GAME_GetUnitLife(pUnit));
		break;
		case UNIT_HPMAX:
			*vp = INT_TO_JSVAL(GAME_GetUnitMaxLife(pUnit));
		break;
		case UNIT_ID1:
			*vp = INT_TO_JSVAL(pUnit->dwID1);
		break;
		case UNIT_ID2:
			*vp = INT_TO_JSVAL(pUnit->dwID2);
		break;
		case UNIT_CLASSID:
			JS_NewNumberValue(cx, pUnit->dwClassId, vp);
		break;
		case UNIT_OWNERSLOT:
			*vp = INT_TO_JSVAL(pUnit->dwOwnerSlot);
		break;
		case UNIT_ISHERO:
			*vp = BOOLEAN_TO_JSVAL(GAME_IsHero(pUnit->dwClassId));
		break;
		case UNIT_X:
			GAME_FillUnitCoords(&pUnit->pInfo, NULL, &Coords);
			JS_NewDoubleValue(cx, Coords.fX, vp);
		break;
		case UNIT_Y:
			GAME_FillUnitCoords(&pUnit->pInfo, NULL, &Coords);
			JS_NewDoubleValue(cx, Coords.fY, vp);
		break;
		case UNIT_ANIMATIONID:
			*vp = INT_TO_JSVAL(GetUnitAnimationId(pUnit));
		break;
		default:
		break;
	}

	return JS_TRUE;
}

INT unit_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
	return JS_TRUE;
}

INT my_getUnit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	myUnit* pUnit = new myUnit;

	if(!pUnit)
		return JS_TRUE;

	memset(pUnit, NULL, sizeof(myUnit));
	pUnit->_dwPrivateType = PRIVATE_UNIT;

	jsint nOwner = -1;
	jsint nClassId = -1;
	jsint nId1 = -1;
	jsint nId2 = -1;
	CHAR* lpszName = NULL;

	if(argc > 0 && JSVAL_IS_INT(argv[0]))
		nOwner = JSVAL_TO_INT(argv[0]);

	if(argc > 1 && JSVAL_IS_INT(argv[1]))
		nClassId = JSVAL_TO_INT(argv[1]);

	if(argc > 1 && JSVAL_IS_STRING(argv[1]))
		lpszName = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));

	if(argc > 2 && JSVAL_IS_INT(argv[2]))
		nId1 = JSVAL_TO_INT(argv[2]);

	if(argc > 3 && JSVAL_IS_INT(argv[3]))
		nId2 = JSVAL_TO_INT(argv[3]);

	if(lpszName)
		strcpy(pUnit->szName, lpszName);

	Unit* lpUnit = FindUnit(nOwner, nClassId, lpszName, nId1, nId2, &pUnit->dwIndex);

	if(!lpUnit)
	{
		delete pUnit;
		return JS_TRUE;
	}

	pUnit->dwOwnerSlot = nOwner;
	pUnit->dwClassId = nClassId;
	pUnit->dwId1 = lpUnit->dwID1;
	pUnit->dwId2 = lpUnit->dwID2;
	
	if(lpszName)
		strcpy(pUnit->szName, lpszName);

	JSObject* jsUnit = JS_NewObject(cx, &unit_class, NULL, NULL);

	if(!jsUnit)
		return JS_TRUE;

	if(!JS_DefineProperties(cx, jsUnit, unit_props))
		return JS_TRUE;
	if(!JS_DefineFunctions(cx, jsUnit, unit_methods)) 
		return JS_TRUE;
	
	JS_SetPrivate(cx, jsUnit, pUnit);

	*rval = OBJECT_TO_JSVAL(jsUnit);

	return JS_TRUE;
}

INT unit_getNext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	myUnit* lpUnit = (myUnit*)JS_GetPrivate(cx, obj);

	if(!lpUnit || IsBadReadPtr(lpUnit, sizeof(myUnit)) || lpUnit->_dwPrivateType != PRIVATE_UNIT)
	{
		JS_ClearScope(cx, obj);
		JS_ValueToObject(cx, JSVAL_NULL, &obj);
		*rval = INT_TO_JSVAL(0);
		return JS_TRUE;
	}

	Unit* pUnit = FindUnit(lpUnit->dwId1, lpUnit->dwId2);

	if(!pUnit)
	{
		JS_ClearScope(cx, obj);
		JS_ValueToObject(cx, JSVAL_NULL, &obj);
		*rval = INT_TO_JSVAL(0);
		return JS_TRUE;
	}

	pUnit = FindNextUnit(lpUnit->dwOwnerSlot, lpUnit->dwClassId, lpUnit->szName, pUnit->dwID1, pUnit->dwID2, &lpUnit->dwIndex);

	if(pUnit)
	{
		lpUnit->dwId1 = pUnit->dwID1;
		lpUnit->dwId2 = pUnit->dwID2;
		JS_SetPrivate(cx, obj, lpUnit);
		*rval = OBJECT_TO_JSVAL(obj);
	}
	else
	{
		JS_ClearScope(cx, obj);
		JS_ValueToObject(cx, JSVAL_NULL, &obj);
		*rval = INT_TO_JSVAL(0);
	}
	
	return JS_TRUE;
}

INT unit_command(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	myUnit* lpUnit = (myUnit*)JS_GetPrivate(cx, obj);

	if(!lpUnit || IsBadReadPtr(lpUnit, sizeof(myUnit)) || lpUnit->_dwPrivateType != PRIVATE_UNIT)
		return JS_TRUE;

	Unit* pUnit = FindUnit(lpUnit->dwId1, lpUnit->dwId2);
	Unit* pTarget = NULL;

	jsdouble fX = 0, fY = 0;
	jsint nCommand = 0;

	if(argc > 2 && JSVAL_IS_NUMBER(argv[0]) && JSVAL_IS_NUMBER(argv[1]) && JSVAL_IS_NUMBER(argv[2]))
	{
		if(JSVAL_IS_INT(argv[0]))
			nCommand = JSVAL_TO_INT(argv[0]);
		else nCommand = *JSVAL_TO_DOUBLE(argv[0]);

		if(JSVAL_IS_DOUBLE(argv[1]))
			fX = *JSVAL_TO_DOUBLE(argv[1]);
		else fX = JSVAL_TO_INT(argv[1]);

		if(JSVAL_IS_DOUBLE(argv[2]))
			fY = *JSVAL_TO_DOUBLE(argv[2]);
		else fY = JSVAL_TO_INT(argv[2]);

	} else if(argc == 2 && JSVAL_IS_NUMBER(argv[0]) && JSVAL_IS_OBJECT(argv[1]))
	{
		myUnit* lpTarget = (myUnit*)JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[1]));

		if(!lpTarget || IsBadReadPtr(lpTarget, sizeof(myUnit)) || lpTarget->_dwPrivateType != PRIVATE_UNIT)
			return JS_TRUE;

		if(JSVAL_IS_INT(argv[0]))
			nCommand = JSVAL_TO_INT(argv[0]);
		else nCommand = *JSVAL_TO_DOUBLE(argv[0]);

		pTarget = FindUnit(lpTarget->dwId1, lpTarget->dwId2);
	}
	else if(argc == 1 && JSVAL_IS_NUMBER(argv[0]))
	{
		if(JSVAL_IS_INT(argv[0]))
			nCommand = JSVAL_TO_INT(argv[0]);
		else nCommand = *JSVAL_TO_DOUBLE(argv[0]);

		CommandUnit(pUnit, nCommand, NULL, NULL, NULL, TRUE);
		return JS_TRUE;
	}

	CommandUnit(pUnit, nCommand, fX, fY, pTarget);

	return JS_TRUE;
}

INT unit_build(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	myUnit* lpUnit = (myUnit*)JS_GetPrivate(cx, obj);

	if(!lpUnit || IsBadReadPtr(lpUnit, sizeof(myUnit)) || lpUnit->_dwPrivateType != PRIVATE_UNIT)
		return JS_TRUE;

	Unit* pUnit = FindUnit(lpUnit->dwId1, lpUnit->dwId2);
	
	if(argc > 2 && (JSVAL_IS_INT(argv[0]) || JSVAL_IS_DOUBLE(argv[0])) && JSVAL_IS_NUMBER(argv[1]) && JSVAL_IS_NUMBER(argv[2]))
	{
		jsdouble fX = 0, fY = 0;
		jsint nCommand = 0;

		if(JSVAL_IS_INT(argv[0]))
			nCommand = JSVAL_IS_INT(argv[0]);
		else nCommand = *JSVAL_TO_DOUBLE(argv[0]);

		if(JSVAL_IS_DOUBLE(argv[1]))
			fX = *JSVAL_TO_DOUBLE(argv[1]);
		else fX = JSVAL_TO_INT(argv[1]);

		if(JSVAL_IS_DOUBLE(argv[2]))
			fY = *JSVAL_TO_DOUBLE(argv[2]);
		else fY = JSVAL_TO_INT(argv[2]);

		// Note, these steps are required. Warcraft only accepts rounded values when you build something!
		CHAR szBuffer[100] = "";
		sprintf_s(szBuffer, 100, "%.0f", fX);
		fX = atof(szBuffer);
		sprintf_s(szBuffer, 100, "%.0f", fY);
		fY = atof(szBuffer);

		CommandUnit_Build(pUnit, fX, fY, nCommand);
	}

	return JS_TRUE;
}